package com.zlyx.easy.api.web;

import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.Map.Entry;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.core.convert.support.DefaultConversionService;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import com.zlyx.easy.api.ApiManager;
import com.zlyx.easy.api.http.HttpMatch;
import com.zlyx.easy.core.loggers.Logger;
import com.zlyx.easy.core.map.DataMap;
import com.zlyx.easy.core.tool.BaseType;
import com.zlyx.easy.core.utils.HttpUtils.HttpResponse;
import com.zlyx.easy.core.utils.JsonUtils;
import com.zlyx.easy.core.utils.MethodUtils;
import com.zlyx.easy.core.utils.RequestUtils;

import io.swagger.annotations.Api;
import io.swagger.util.Json;
import io.swagger.util.PrimitiveType;

@Api(hidden = true)
@Controller
@RequestMapping("method-api")
public class MethodInvorkeController {

	@RequestMapping(value = "/{interfaceClass}/{methodName}", produces = "application/json; charset=utf-8")
	@ResponseBody
	public ResponseEntity<String> invokeMethod(@PathVariable("interfaceClass") String interfaceClass,
			@PathVariable("methodName") String methodName, HttpServletRequest request, HttpServletResponse response)
			throws Exception {
		return invokeMethod(interfaceClass, methodName, null, request, response);
	}

	@RequestMapping(value = "/{interfaceClass}/{methodName}/{operationId}", produces = "application/json; charset=utf-8")
	@ResponseBody
	public ResponseEntity<String> invokeMethod(@PathVariable("interfaceClass") String interfaceClass,
			@PathVariable("methodName") String methodName, @PathVariable("operationId") String operationId,
			HttpServletRequest request, HttpServletResponse response) {
		try {
			// 判断该接口是否注册
			Entry<Class<?>, Object> entry = ApiManager.getRef(interfaceClass);
			if (null == entry) {
				Logger.debug("No Ref FOUND.");
				return new ResponseEntity<String>(HttpStatus.BAD_REQUEST);
			}
			// 判断调用方法是否存在
			Object ref = entry.getValue();
			HttpMatch httpMatch = new HttpMatch(entry.getKey(), ref.getClass());
			Method[] interfaceMethods = httpMatch.findInterfaceMethods(methodName);
			Method method = null;
			if (null != interfaceMethods && interfaceMethods.length > 0) {
				Method[] refMethods = httpMatch.findRefMethods(interfaceMethods, operationId, request.getMethod());
				method = httpMatch.matchRefMethod(refMethods, methodName, request.getParameterMap().keySet());
			}
			if (null == method) {
				Logger.debug("No Service Method FOUND.");
				return new ResponseEntity<String>(HttpStatus.BAD_REQUEST);
			}
			// 判断该实现类是否存在接口
			if (ref.getClass().getInterfaces().length > 0) {
				method = ref.getClass().getMethod(method.getName(), method.getParameterTypes());
				if (null == method) {
					Logger.debug("No Service Method FOUND.");
					return new ResponseEntity<String>(HttpStatus.BAD_REQUEST);
				}
			}
			Logger.debug("Invoke method:{},parameter:{}", method, Json.pretty(request.getParameterMap()));

			// 调用spring提供的方法获取参数名称
			method = MethodUtils.getRealMethod(entry.getKey(), method);
			String[] parameterNames = MethodUtils.getParameterNames(method);
			Object result = null;
			if ((null == parameterNames || parameterNames.length == 0) && method.getParameterCount() == 0) {
				result = method.invoke(ref);
			} else {
				Object[] args = new Object[method.getParameterCount()];
				Type[] parameterTypes = method.getGenericParameterTypes();
				Class<?>[] parameterClazz = method.getParameterTypes();
				DataMap dataMap = DataMap.newMap(RequestUtils.getParamsMap(), "：");
				String name = null;
				for (int i = 0; i < method.getParameterCount(); i++) {
					if (parameterNames != null) {
						name = parameterNames[i];
					} else {
						name = method.getParameters()[i].getName();
					}
					if (request.getParameter(name) == null) {
						if (BaseType.isJavaType(parameterClazz[i].getSimpleName())) {
							args[i] = -1;
						} else {
							args[i] = JsonUtils.fromJson(dataMap.get(name), (Class<?>) parameterTypes[i]);
						}
					} else {
						args[i] = suggestPrameterValue(parameterTypes[i], parameterClazz[i],
								request.getParameter(name));
					}
				}
				result = method.invoke(ref, args);
			}
			return ResponseEntity.ok(Json.mapper().writeValueAsString(result));
		} catch (Exception e) {
			Logger.err(e);
			return ResponseEntity.ok(HttpResponse.failure(e).toString());
		}
	}

	private Object suggestPrameterValue(Type type, Class<?> cls, String parameter) throws Exception {
		PrimitiveType fromType = PrimitiveType.fromType(type);
		if (null != fromType) {
			DefaultConversionService service = new DefaultConversionService();
			boolean actual = service.canConvert(String.class, cls);
			if (actual) {
				return service.convert(parameter, cls);
			}
		} else {
			if (null == parameter)
				return null;
			try {
				return Json.mapper().readValue(parameter, cls);
			} catch (Exception e) {
				throw new IllegalArgumentException(
						"The parameter value [" + parameter + "] should be json of [" + cls.getName() + "] Type.", e);
			}
		}
		return Class.forName(cls.getName()).newInstance();
	}

}
